#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cstdio>
#include<cerrno>
#include<cstring>
#include<cstdlib>
#include <unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<assert.h>

// 使用共享内存实现进程间通信
using namespace std;

// 让两个进程使用同样的两个参数去生成key【用ftok】
#define PATHNAME "."
#define PROJ_ID 0x66
#define SIZE 4096

// 利用命名管道实现对共享内存中数据的保护
#define NAMED_PIPE "./mypipe" // 创建管道的路径

key_t getKey()
{
    key_t key = ftok(PATHNAME, PROJ_ID);
    // 判断ftok是否成功调用
    if(key < 0)
    {
        // c++的cin cout cerr 就是三个类，语言级别的文件流，底层一定封装着0、1、2这三个文件描述符
        cerr << errno << ": " << strerror(errno) <<  endl;
        exit(1);
    }
    return key;
}

// 由于两个进程无论是创建还是获取共享内存，用的都是shmget函数，因此直接对该函数进行封装
int HelpShm(key_t k, int shmflg)
{
    int shmid = shmget(k, SIZE, shmflg); //创建共享内存
    if(shmid < 0)
    {
        cerr << errno << ": " << strerror(errno) << endl;
        exit(2);
    }
    return shmid;
}

int CreateShm(key_t k)
{
    return HelpShm(k, IPC_CREAT | IPC_EXCL | 0600); //0600是共享内存创建时的权限
}

int GetShm(key_t k)
{
    return HelpShm(k, IPC_CREAT);
}

// 将共享内存和进程地址空间关联
void* attachShm(int shmid)
{
    void* mem = shmat(shmid, nullptr, 0);
    if((long long)mem == -1L) // 这里是64位系统，指针为8个字节的大小
    {
        cerr << errno << ": " << strerror(errno) << endl;
        exit(1);
    }
    return mem;
}

//去关联
void detachShm(const void* shmaddr)
{
    if(shmdt(shmaddr) == -1)
    {
        cerr << errno << ": " << strerror(errno) << endl;
        exit(1);
    }
}


void DelShm(int shmid)
{
    if(shmctl(shmid, IPC_RMID, nullptr) == -1)
    {
        cerr << errno << ": " << strerror(errno) << endl;
    }
}


// 创建命名管道
bool createFifo(const string& path)
{
    umask(0);
    int n = mkfifo(path.c_str(), 0600); //0600 只能我自己使用这个管道
    if(n == 0)
    {
        return true;
    }
    else
    {
        cout << "errno: " << errno << strerror(errno) << endl;
        return false;
    }
}

//删除命名管道
void removeFifo(const string& path)
{
    int n = unlink(path.c_str()); // 转化为char*
    assert(n == 0); // assert是debug模式下才生效的，realise模式下无效
    (void)n; // 在realise模式下，n没有被使用，这里就是告诉编译器不要警告，这里没有问题
}